package com.laity.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.single.DoubleCheckedLockingSingleton
 * @Date: 2023年11月20日 22:31
 * @Description: 双重检查锁定：解决懒汉式单例在多线程下的问题
 * 双重检测锁模式的 懒汉式单例，DCL懒汉式
 */

public class DoubleCheckedLockingSingleton {

    // 红绿灯 - 如果加密处理会使其更安全
    private static boolean laity = false;

    private DoubleCheckedLockingSingleton() {
        // 私有构造函数，防止外部直接实例化

        // 解决反射带来封装性的破坏
        synchronized (StaticInnerClassSingleton.class) {
            if (!laity) {
                laity = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏哦！");
            }
        }

        System.out.println(Thread.currentThread().getName() + "-ok");
    }

    private static volatile DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;

    public static DoubleCheckedLockingSingleton getInstance() {
        if (doubleCheckedLockingSingleton == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (doubleCheckedLockingSingleton == null) {
                    // 不是一个原子性操作，
                    /*
                    不是原子性操作会经过哪些步骤？
                    1.分配内存空间
                    2.执行构造方法，初始化对象
                    3.把对象指向内存空间，才能保证这个对象new完了
                    所以在多线程的情况下，可能会出现指令重排现象
                    就是原本 123 顺序，可事实确实 132 顺序，执行的顺序不一致

                    线程1：执行的顺序是 132
                    在线程1执行到3时，进来了一个线程2，线程2一看都执行到3了，
                    导致doubleCheckedLockingSingleton!=null，直接return doubleCheckedLockingSingleton;
                    但是其实此时这个对象还没有完成构造，那么这个对象就是虚无的

                    所以必须在 private static DoubleCheckedLockingSingleton doubleCheckedLockingSingleton;
                    基础上，添加volatile
                     */
                    doubleCheckedLockingSingleton = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return doubleCheckedLockingSingleton;
    }

    // 多线程下测试
    public static void main(String[] args) {
        // 之前我是创建了对象，那么现在我根本就不去创建这个对象
        // DoubleCheckedLockingSingleton instance = DoubleCheckedLockingSingleton.getInstance();
        try {
            Field declaredFields = DoubleCheckedLockingSingleton.class.getDeclaredField("laity");
            declaredFields.setAccessible(true);

            Constructor<DoubleCheckedLockingSingleton> declaredConstructor = DoubleCheckedLockingSingleton.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            // 两个对象都是基于反射new出来的
            DoubleCheckedLockingSingleton doubleCheckedLockingSingleton1 = declaredConstructor.newInstance();
            declaredFields.set(doubleCheckedLockingSingleton1, false);
            DoubleCheckedLockingSingleton doubleCheckedLockingSingleton2 = declaredConstructor.newInstance();
            System.out.println(doubleCheckedLockingSingleton1 == doubleCheckedLockingSingleton2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
